﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;

namespace EasyComm
{
    [CollectionDataContract]
    public class ThreadSafeCollection<T> : IList<T>
        where T : class
    {
        public event ThreadSafeCollectionChangeEventHandler<T> Change;

        protected ReaderWriterLockSlim InnerLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        private List<T> _innerList = new List<T>();

        public virtual T this[int index]
        {
            get
            {
                InnerLock.EnterReadLock();
                try
                {
                    return _innerList[index];
                }
                finally
                {
                    InnerLock.ExitReadLock();
                }
            }
            set
            {
                InnerLock.EnterUpgradeableReadLock();
                try
                {
                    _innerList[index] = value;
                    OnChange(value, index, ChangeType.Modified);
                }
                finally
                {
                    InnerLock.ExitUpgradeableReadLock();
                }
            }
        }

        public virtual int IndexOf(T item)
        {
            InnerLock.EnterReadLock();
            try
            {
                return _innerList.IndexOf(item);
            }
            finally
            {
                InnerLock.ExitReadLock();
            }
        }

        public virtual void Insert(int index, T item)
        {
            InnerLock.EnterWriteLock();
            try
            {
                if (index >= 0 && index <= _innerList.Count)
                {
                    _innerList.Insert(index, item);
                    OnChange(item, index, ChangeType.Added);
                }
            }
            finally
            {
                InnerLock.ExitWriteLock();
            }
        }

        public virtual void RemoveAt(int index)
        {
            InnerLock.EnterWriteLock();
            try
            {
                if (index >= 0 && index < _innerList.Count)
                {
                    T unit = _innerList[index];
                    if (unit as IDisposable != null)
                    {
                        (unit as IDisposable).Dispose();
                    }
                    _innerList.RemoveAt(index);
                    OnChange(unit, index, ChangeType.Removed);
                }
            }
            finally
            {
                InnerLock.ExitWriteLock();
            }
        }

        public virtual void Add(T item)
        {
            InnerLock.EnterWriteLock();
            try
            {
                _innerList.Add(item);
                OnChange(item, _innerList.Count - 1, ChangeType.Added);
            }
            finally
            {
                InnerLock.ExitWriteLock();
            }
        }

        public virtual void Clear()
        {
            InnerLock.EnterWriteLock();
            try
            {
                foreach (T unit in this)
                {
                    if (unit as IDisposable != null)
                    {
                        (unit as IDisposable).Dispose();
                    }
                }

                _innerList.Clear();
                OnChange(null, -1, ChangeType.RemovedAll);
            }
            finally
            {
                InnerLock.ExitWriteLock();
            }
        }

        public virtual bool Contains(T item)
        {
            InnerLock.EnterReadLock();
            try
            {
                return _innerList.Contains(item);
            }
            finally
            {
                InnerLock.ExitReadLock();
            }
        }

        public virtual void CopyTo(T[] array, int arrayIndex)
        {
            InnerLock.EnterReadLock();
            try
            {
                _innerList.CopyTo(array, arrayIndex);
            }
            finally
            {
                InnerLock.ExitReadLock();
            }
        }

        public virtual int Count
        {
            get
            {
                InnerLock.EnterReadLock();
                try
                {
                    return _innerList.Count;
                }
                finally
                {
                    InnerLock.ExitReadLock();
                }
            }
        }

        public virtual bool IsReadOnly
        {
            get { return false; }
        }

        public virtual bool Remove(T item)
        {
            InnerLock.EnterWriteLock();
            try
            {
                bool b = false;
                int idx = _innerList.IndexOf(item);
                if (idx >= 0)
                {
                    if (item as IDisposable != null)
                    {
                        (item as IDisposable).Dispose();
                    }
                    b = _innerList.Remove(item);
                    if (b)
                    {
                        OnChange(item, idx, ChangeType.Removed);
                    }
                }
                return b;
            }
            finally
            {
                InnerLock.ExitWriteLock();
            }
        }

        public virtual IEnumerator<T> GetEnumerator()
        {
            InnerLock.EnterReadLock();
            try
            {
                return _innerList.GetEnumerator();
            }
            finally
            {
                InnerLock.ExitReadLock();
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            InnerLock.EnterReadLock();
            try
            {
                return _innerList.GetEnumerator();
            }
            finally
            {
                InnerLock.ExitReadLock();
            }
        }

        public virtual void OnChange(T item, int index, ChangeType changeType)
        {
            if (Change != null)
            {
                Change(this, new ThreadSafeCollectionEventArgs<T>(item, index, changeType));
            }
        }

    }
}
